/*
 * Copyright (C) 2009 Android Shuffle Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.dodgybits.shuffle.android.editor.activity;

import org.dodgybits.android.shuffle.R;
import org.dodgybits.shuffle.android.core.activity.flurry.FlurryEnabledActivity;
import org.dodgybits.shuffle.android.core.model.Entity;
import org.dodgybits.shuffle.android.core.model.Id;
import org.dodgybits.shuffle.android.core.model.encoding.EntityEncoder;
import org.dodgybits.shuffle.android.core.model.persistence.EntityPersister;
import org.dodgybits.shuffle.android.core.view.MenuUtils;
import org.dodgybits.shuffle.android.list.activity.State;
import org.dodgybits.shuffle.android.preference.model.Preferences;

import android.content.ContentUris;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

/**
 * A generic activity for editing an item in a database. This can be used either
 * to simply view an item (Intent.VIEW_ACTION), view and edit an item
 * (Intent.EDIT_ACTION), or create a new item (Intent.INSERT_ACTION).
 */
public abstract class AbstractEditorActivity<E extends Entity> extends
		FlurryEnabledActivity implements View.OnClickListener,
		View.OnFocusChangeListener {

	private static final String cTag = "AbstractEditorActivity";

	protected int mState;
	protected Uri mUri;
	protected Cursor mCursor;
	protected E mOriginalItem;
	protected Intent mNextIntent;

	@Override
	protected void onCreate(Bundle icicle) {
		Log.d(cTag, "onCreate+");
		super.onCreate(icicle);

		processIntent();

		setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
		setContentView(getContentViewResId());

		addSavePanelListeners();
		Log.d(cTag, "onCreate-");
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		switch (keyCode) {
		case KeyEvent.KEYCODE_BACK:
			doSaveAction();
			break;
		}

		return super.onKeyDown(keyCode, event);
	}

	@Override
	public void finish() {
		if (mNextIntent != null) {
			startActivity(mNextIntent);
		}
		super.finish();
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		super.onPrepareOptionsMenu(menu);
		MenuItem item = menu.findItem(MenuUtils.SYNC_ID);
		if (item != null) {
			item.setVisible(Preferences.validateTracksSettings(this));
		}
		return true;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);

		MenuUtils.addEditorMenuItems(menu, mState);
		MenuUtils.addPrefsHelpMenuItems(this, menu);

		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle all of the possible menu actions.
		switch (item.getItemId()) {
		case MenuUtils.SAVE_ID:
			doSaveAction();
			break;
		case MenuUtils.SAVE_AND_ADD_ID:
			// create an Intent for the new item based on the current item
			startActivity(getInsertIntent());
			doSaveAction();
			break;
		case MenuUtils.DELETE_ID:
			doDeleteAction();
			finish();
			break;
		case MenuUtils.DISCARD_ID:
			doRevertAction();
			break;
		case MenuUtils.REVERT_ID:
			doRevertAction();
			break;
		}
		if (MenuUtils.checkCommonItemsSelected(item, this, MenuUtils.INBOX_ID)) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		E item = createItemFromUI(false);
		saveItem(outState, item);
	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState) {
		super.onRestoreInstanceState(savedInstanceState);
		E item = restoreItem(savedInstanceState);
		updateUIFromItem(item);
	}

    @Override
	public void onFocusChange(View v, boolean hasFocus) {
		// Because we're emulating a ListView, we need to setSelected() for
		// views as they are focused.
		v.setSelected(hasFocus);
	}

	public void onClick(View v) {
		switch (v.getId()) {

		case R.id.saveButton:
			doSaveAction();
			break;

		case R.id.discardButton:
			doRevertAction();
			break;
		}
	}

	protected void doSaveAction() {
		// Save or create the contact if needed
		Uri result = null;
		switch (mState) {
		case State.STATE_EDIT:
			result = save();
			break;

		case State.STATE_INSERT:
			result = create();
			break;

		default:
			Log.e(cTag, "Unknown state in doSaveAction: " + mState);
			break;
		}

		if (result == null) {
			setResult(RESULT_CANCELED);
		} else {
			setResult(RESULT_OK, new Intent().setData(result));
		}
		finish();
	}

	/**
	 * Take care of canceling work on a item. Deletes the item if we had created
	 * it, otherwise reverts to the original text.
	 */
	protected void doRevertAction() {
		if (mCursor != null) {
			if (mState == State.STATE_EDIT) {
				// Put the original item back into the database
				mCursor.close();
				mCursor = null;
				getPersister().update(mOriginalItem);
			} else if (mState == State.STATE_INSERT) {
				// if inserting, there's nothing to delete
			}
		}
		setResult(RESULT_CANCELED);
		finish();
	}

	/**
	 * Take care of deleting a item. Simply deletes the entry.
	 */
	protected void doDeleteAction() {
		// if inserting, there's nothing to delete
		if (mState == State.STATE_EDIT && mCursor != null) {
			mCursor.close();
			mCursor = null;
			
	        Id id = Id.create(ContentUris.parseId(mUri));
	        getPersister().updateDeletedFlag(id, true);
		}
	}
	
	protected final void showSaveToast() {
		String text;
		if (mState == State.STATE_EDIT) {
			text = getResources().getString(R.string.itemSavedToast,
					getItemName());
		} else {
			text = getResources().getString(R.string.itemCreatedToast,
					getItemName());
		}
		Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
	}

	protected boolean isValid() {
		return true;
	}

	protected Uri create() {
		Uri uri = null;
		if (isValid()) {
			E item = createItemFromUI(true);
			uri = getPersister().insert(item);
			showSaveToast();
		}
		return uri;
	}

	protected Uri save() {
		Uri uri = null;
		if (isValid()) {
			E item = createItemFromUI(true);
			getPersister().update(item);
			showSaveToast();
			uri = mUri;
		}
		return uri;
	}

	protected final E restoreItem(Bundle icicle) {
		return getEncoder().restore(icicle);
	}

	protected final void saveItem(Bundle outState, E item) {
		getEncoder().save(outState, item);
	}

	/**
	 * @return id of layout for this view
	 */
	abstract protected int getContentViewResId();

	abstract protected E createItemFromUI(boolean commitValues);

	abstract protected void updateUIFromItem(E item);

	abstract protected void updateUIFromExtras(Bundle extras);

	abstract protected EntityPersister<E> getPersister();

	abstract protected EntityEncoder<E> getEncoder();

	abstract protected Intent getInsertIntent();

	abstract protected CharSequence getItemName();

	private void processIntent() {
		final Intent intent = getIntent();
		// Do some setup based on the action being performed.
		final String action = intent.getAction();
		mUri = intent.getData();
		if (action.equals(Intent.ACTION_EDIT)) {
			// Requested to edit: set that state, and the data being edited.
			mState = State.STATE_EDIT;
		} else if (action.equals(Intent.ACTION_INSERT)) {
			// Requested to insert: set that state, and create a new entry
			// in the container.
			mState = State.STATE_INSERT;
		} else {
			// Whoops, unknown action! Bail.
			Log.e(cTag, "Unknown action " + action + ", exiting");
			finish();
			return;
		}
	}

	private void addSavePanelListeners() {
		// Setup the bottom buttons
		View view = findViewById(R.id.saveButton);
		view.setOnClickListener(this);
		view = findViewById(R.id.discardButton);
		view.setOnClickListener(this);
	}

}
